// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#include <limits>
#include <melon/benchmark.h>
#include "melon/Portability.h"
#include <melon/random.h>
#include <melon/varint.h>
#include "melon/init/Init.h"
#include <melon/lang/bits.h>
#include <pollux/common/exception/exception.h>

#include <pollux/type/filter.h>

using namespace kumo::pollux;
using namespace kumo::pollux::common;

std::vector<int64_t> sparseValues;
std::vector<int64_t> denseValues;
std::unique_ptr<BigintValuesUsingHashTable> filter;

int32_t run1x64(const std::vector<int64_t>& data) {
  int32_t count = 0;
  for (auto i = 0; i < data.size(); ++i) {
    count += filter->testInt64(data[i]);
  }
  return count;
}

int32_t run4x64(const std::vector<int64_t>& data) {
  constexpr int kStep = ksimd::batch<int64_t>::size;
  int32_t count = 0;
  assert(data.size() % kStep == 0);
  for (auto i = 0; i < data.size(); i += kStep) {
    auto result = filter->testValues(ksimd::load_unaligned(data.data() + i));
    count += __builtin_popcount(simd::toBitMask(result));
  }
  return count;
}

BENCHMARK(scalarDense) {
  melon::doNotOptimizeAway(run1x64(denseValues));
}

BENCHMARK_RELATIVE(simdDense) {
  melon::doNotOptimizeAway(run4x64(denseValues));
}

BENCHMARK(scalarSparse) {
  melon::doNotOptimizeAway(run1x64(sparseValues));
}

BENCHMARK_RELATIVE(simdSparse) {
  melon::doNotOptimizeAway(run4x64(sparseValues));
}

int32_t main(int32_t argc, char* argv[]) {
  constexpr int32_t kNumValues = 1000000;
  constexpr int32_t kFilterValues = 1000;
  melon::Init init{&argc, &argv};

  std::vector<int64_t> filterValues;
  filterValues.reserve(kFilterValues);
  for (auto i = 0; i < kFilterValues; ++i) {
    filterValues.push_back(i * 1000);
  }
  filter = std::make_unique<BigintValuesUsingHashTable>(
      filterValues.front(), filterValues.back(), filterValues, false);
  denseValues.resize(kNumValues);
  sparseValues.resize(kNumValues);
  for (auto i = 0; i < kNumValues; ++i) {
    denseValues[i] = (melon::Random::rand32() % 3000) * 1000;
    sparseValues[i] = (melon::Random::rand32() % 100000) * 1000;
  }

  POLLUX_CHECK_EQ(run1x64(denseValues), run4x64(denseValues));
  POLLUX_CHECK_EQ(run1x64(sparseValues), run4x64(sparseValues));
  melon::runBenchmarks();
  return 0;
}
